#include "MasteTestClient.hpp"
#include "cybertron/network/Message.hpp"
#include "cybertron/core/UtilStateHelper.hpp"
//#include <direct.h>
#include "cybertron/core/UtilFile.hpp"
#include "cybertron/core/UtilString.hpp"
using namespace cybertron;
MasterTestClient::MasterTestClient():
	mState(EServiceState_Stop)
{
	zeroMembers();
	std::thread thread(&MasterTestClient::run, this);
	thread.detach();
}

MasterTestClient::~MasterTestClient()
{
	clearThis();
}

void MasterTestClient::zeroMembers()
{
	mbLogon = false;
}

void MasterTestClient::clearThis()
{

}
bool MasterTestClient::initialize(int argc, char * argv[])
{
	cybertron::setupLogFile("MasterTestClient");
	bool ret = Connect();
	if (ret) {
		mState = EServiceState_Work;
		sendRegisterNode();
	}
	return ret;
}

char *randstr(char *str, const int len)
{
	srand(time(NULL));
	int i;
	for (i = 0; i < len; ++i)
	{
		switch ((rand() % 3))
		{
		case 1:
			str[i] = 'A' + rand() % 26;
			break;
		case 2:
			str[i] = 'a' + rand() % 26;
			break;
		default:
			str[i] = '0' + rand() % 10;
			break;
		}
	}
	str[++i] = '\0';
	return str;
}
void MasterTestClient::test()
{
	mpConfig = std::make_shared<MasterConfig>();
	mpConfig->load("bundle.json",true);
	Master::TaskPreAllocateFromCommander info;
	char task_id[128] = { 0 };
	string task_str = randstr(task_id, 8);
	info.set_taskid(task_str);

	//info.mutable_taskconfig()->set_tasktimeout(500);
	//info.mutable_taskconfig()->set_caseid("");
	//info.mutable_taskconfig()->set_casename("");
	//info.mutable_taskconfig()->set_worldconfigjson("");

	Master::AllocNodeWorkItem* pAllocNodeWorkItem = 0;
	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronNodeTimer);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronBridgeIO);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronVehicleDynamic);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronTrafficProvider);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronBridgeSimOneDriver);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronVisualizer);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Unnecessary);

	mpClient->send(Master::ETaskPreAllocateFromCommander, info);

	//mpClient->re
	//Message result;
	//uint16_t messageId = Master::ETaskProAllocateRetFromCommander;
	//if (!processMessagesUntil(&messageId, &result)) {
	//	logInfo("ETaskPreAllocateFromCommander falied");
	//	return;
	//}

	//Master::TaskProAllocateRetFromCommander ret;
	//if (!result.toProtobuf(ret)) {
	//	return;
	//}
	//logInfo("result taskId:%s message:%s errorCode:%s", ret.taskid(), ret.message(), ret.errorcode());
}

bool MasterTestClient::Connect()
{
	logInfo("Connected to server!");
	mHostIp = "127.0.0.1";
	mHostPort = 4500;

	mpClient = std::make_shared<SocketTcpClient>();
	logInfo("Connecting to %s:%d...", mHostIp.c_str(), mHostPort);
	try
	{
		mpClient->connectBlocked(mHostIp.c_str(), mHostPort);
	}
	catch (const std::exception&)
	{
		logError("Connecting to %s:%d...", mHostIp.c_str(), mHostPort);
		return false;
	}
	logInfo("connecting master server(%s:%d) is successful", mHostIp.c_str(), mHostPort);
	mpClient->setHandlerMessage([this](Message& msg) {
		this->onServerMessage(msg);
	});
	mpClient->setHandlerDisconnected([this]() {
		this->onDisconnected();
	});
	mpClient->setHandlerConnected([this]() {
		logInfo("=====> xxx");
		//this->onDisconnected();
	});
	return true;
}

void MasterTestClient::onDisconnected()
{
}

void MasterTestClient::onServerMessage(Message& msg)
{
	mOneLoopServerMessageCounter++;
	std::uint16_t msgId = msg.parseMsgId();
	logInfo("receive message--------------->:%d", msgId);

	switch (msgId)
	{
	case Master::ENodeRegisterAtMasterRet:
		onFromMasterNodeRegisterRet(msg);
		break;
	case Master::ECommand2AgentFromRetCommander:
		onFromMasterCommand2AgentRet(msg);
		break;
	case Master::EGetAgentHardwareInfoFromRetCommander:
		onFromMasterGetAgentHardwareInfoRet(msg);
		break;
	case Master::ETaskPreAllocateRetFromCommander:
		onFromMasterTaskProAllocateRet(msg);
		break;
	case Master::ETaskRecyclePreAllocateRetFromCommander:
		onFromMasterTaskRecycleProAllocateRet(msg);
		break;
	case Master::ETaskStartRetFromCommander:
		onFromMasterTaskStartRet(msg);
		break;
	case Master::ETaskStopRetFromCommander:
		onFromTaskStopRet(msg);
		break;
	case Master::EWorkNodeStartRetFromCommander:
		onFromWorkNodeStartRet(msg);
		break;
	case Master::EWorkNodeStopRetFromCommander:
		onFromWorkNodeStopRet(msg);
		break;
	case Common::ESyncWorkNodeDynamicConfig:
		onFromSyncWorkNodeDynamicConfig(msg);
		break;
		
	case Master::EAgentStatusChange:
		onFromAgentStatusChange(msg);
		break;
	case Master::ETaskStateChange:
		onFromTaskStateChange(msg);
		break;

	case Master::EWorkStausChange:
		onFromWorkStatusChange(msg);
		break;

	case Master::EWorkStateChange:
		onFromWorkStateChange(msg);
		break;
	case Master::EWorkProgessChange:
		onFromWorkProgessChange(msg);
		break;
	case Master::EEndTaskReasonChange:
		onFromEndTaskReasonChange(msg);
		break;
	default:
		// ignore
		break;
	}
}

bool MasterTestClient::processMessagesUntil(
	uint16_t* pDesiredMessageIds,
	Message* pDesiredMessages,
	size_t numDesiredMessages,
	int timeoutMilliseconds,
	bool msgOnebyOne)
{
	if (numDesiredMessages == 0)
	{
		return true;
	}
	mNumDesiredMessages = 0;
	mpDesiredMessageIds = nullptr;
	mpDesiredMessages = nullptr;
	mNumReceivedDesiredMessages = 0;

	if (mpClient == nullptr)
	{
		logInfo("Process messages failed, socket invalid.");
		//setDisconnected();
		return false;
	}
	if (mpClient->getStatus() != ESocketStatus_Connected)
	{
		logError("Process messages failed, socket still connecting.");
		//setDisconnected();
		return false;
	}

	mNumDesiredMessages = numDesiredMessages;
	mpDesiredMessageIds = pDesiredMessageIds;
	mpDesiredMessages = pDesiredMessages;
	for (size_t i = 0; i < mNumDesiredMessages; ++i)
	{
		mpDesiredMessages[i].clear();
	}

	std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();
	for (; ; )
	{
		mOneLoopServerMessageCounter = 0;
		mpClient->onTimer(msgOnebyOne);
		if (mpClient->getStatus() != ESocketStatus_Connected)
		{
			//onDisconnected();
			logError("Process messages failed, socket not connected.");
			return false;
		}
		// break if we got the required messages.
		if (mNumReceivedDesiredMessages >= mNumDesiredMessages) {
			return true;
		}
		// batch process, just return
		if (!msgOnebyOne) {
			return false;
		}

		if (timeoutMilliseconds > 0)
		{
			std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
			std::chrono::nanoseconds span = (t1 - t0);
			if (span.count() / 1000000 > timeoutMilliseconds)
			{
				logInfo("Process messages failed, timeout. Not received message:");
				for (int i = 0; i < mNumDesiredMessages; i++)
				{
					if (mpDesiredMessages[i].parseMsgId() == mpDesiredMessageIds[i])
						continue;
				}
				mNumDesiredMessages = 0;
				mpDesiredMessageIds = nullptr;
				mpDesiredMessages = nullptr;
				mNumReceivedDesiredMessages = 0;
				return false;
			}
		}
		else if (mOneLoopServerMessageCounter == 0) {
			// wait forever case, if no message handled, sleep for a while to yield CPU.
			std::this_thread::sleep_for(std::chrono::milliseconds(1));
		}
	}
	return true;
}

void MasterTestClient::run()
{
	while (1){
		if (mState == EServiceState_Work){
			mpClient->onTimer(true);
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(1));
	}
}

//////////////////////////test///////////////////////////
void MasterTestClient::sendRegisterNode()
{
	if (mpClient == nullptr)
	{
		return;
	}
	Master::NodeInfoAtMaster reg;
	reg.set_type(Master::EMasterNodeTypeCommander);
	reg.set_nodeid("master_commander");
	reg.set_version("1");
	reg.set_desc("cybertron master test");
	reg.set_data("true");

	mpClient->send(Master::ENodeRegisterAtMaster, reg);
	logInfo("------send ENodeRegisterAtMaster");
}
void MasterTestClient::onFromMasterNodeRegisterRet(cybertron::Message& msg) {

	Master::NodeInfoAtMasterRet result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	if (result.code() == 0)
	{
		logInfo("registerNode sucessful");
		mbLogon = true;
	}
	logInfo("------receive MasterNodeRegisterRet");
}

//send command
void MasterTestClient::sendCommand2Agent() {
}
void MasterTestClient::onFromMasterCommand2AgentRet(cybertron::Message& msg) {
}
void MasterTestClient::onFromMasterGetAgentHardwareInfoRet(cybertron::Message& msg) {
	logInfo("---2----EGetAgentHardwareInfoRet-------");
	Master::GetAgentHardwareInfoFromRetCommander info;
	if (!msg.toProtobuf(info))
	{
		return;
	}
	
	auto errorcode = info.errorcode();	
	Common::SimOneSystemInfo systemInfo = info.hardwareinfo();
	
}
void MasterTestClient::GetAgentHardwareInfo()
{
	logInfo("---1----GetAgentHardwareInfo-------");
	Master::GetAgentHardwareInfoFromCommander info;
	info.set_toid("11111111");
	info.set_fromid("master_commander");
	mpClient->send(Master::EGetAgentHardwareInfoFromCommander, info);
}
//task ProAllocate
void MasterTestClient::sendTaskProAllocate() {
	logInfo("---1----sendTaskProAllocate-------");
	Master::TaskPreAllocateFromCommander info;

	char task_id[128] = { 0 };
	string task_str = randstr(task_id, 8);
	info.set_taskid(task_str);
	mTaskId = task_str;
	Master::AllocNodeWorkItem* pAllocNodeWorkItem = 0;
	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronNodeTimer);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);
	pAllocNodeWorkItem->set_worknodeid("");
	pAllocNodeWorkItem->set_agentid("");
	pAllocNodeWorkItem->set_tag("");

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronBridgeIO);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);
	pAllocNodeWorkItem->set_worknodeid("");
	pAllocNodeWorkItem->set_agentid("");
	pAllocNodeWorkItem->set_tag("");

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronVehicleDynamic);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);
	pAllocNodeWorkItem->set_worknodeid("");
	pAllocNodeWorkItem->set_agentid("");
	pAllocNodeWorkItem->set_tag("");

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronVehicleDynamic);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);
	pAllocNodeWorkItem->set_worknodeid("");
	pAllocNodeWorkItem->set_agentid("");
	pAllocNodeWorkItem->set_tag("");


	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronTrafficProvider);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);
	pAllocNodeWorkItem->set_worknodeid("");
	pAllocNodeWorkItem->set_agentid("");
	pAllocNodeWorkItem->set_tag("");

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronBridgeSimOneDriver);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Common);
	pAllocNodeWorkItem->set_worknodeid("");
	pAllocNodeWorkItem->set_agentid("");
	pAllocNodeWorkItem->set_tag("");

	pAllocNodeWorkItem = info.add_nodeworklist();
	pAllocNodeWorkItem->set_worktype(Common::EWorkNodeType_CybertronVisualizer);
	pAllocNodeWorkItem->set_flag(Common::EWorkNodeFlag_Unnecessary);
	pAllocNodeWorkItem->set_worknodeid("");
	pAllocNodeWorkItem->set_agentid("");
	pAllocNodeWorkItem->set_tag("");


	
	mpClient->send(Master::ETaskPreAllocateFromCommander, info);

}
void MasterTestClient::onFromMasterTaskProAllocateRet(cybertron::Message& msg) {
	
	logInfo("----2---onFromMasterTaskProAllocateRet-------");
	Master::TaskPreAllocateRetFromCommander result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	string taskId = result.taskid();
	
	Common::ESimOneErrorCode error_code = result.errorcode();
	if (error_code == Common::ESimOneOK) {
		mTaskId = taskId;
	}
	string message = result.message();
	logInfo("[task proallocate]task id:%s error_code:%d,message:%s", taskId.c_str(), error_code, message.c_str());
}

//task Recycle
void MasterTestClient::sendTaskRecycleProAllocate() {
}
void MasterTestClient::onFromMasterTaskRecycleProAllocateRet(cybertron::Message& msg) {


}
bool getRawString(string path, string& result) {
	std::vector<std::uint8_t> temp;
	if (!UtilFile::tryLoad(path, temp)) {
		return false;
	}
	int len = temp.size();
	char* buffer = new char[len + 1];
	for (size_t i = 0; i < len; i++)
	{
		buffer[i] = temp[i];
	}
	string raw = buffer;
	delete buffer;
	UtilString::encodeBase64Lz4Str(raw, result);
	return true;
}
//task start
void MasterTestClient::sendTaskStart() {
	Master::TaskStartFromCommander info;
	info.set_taskid(mTaskId);
	info.mutable_config()->set_tasktimeout(5000000000);
	info.mutable_config()->set_caseid("test case");
	info.mutable_config()->set_casename("test case");
	
	//info.config.se
	string world_json;
	if (!getRawString("D:\\simonetest\\2\\\\world.txt", world_json)) {
		return ;
	}
	info.set_worldconfigjson(world_json);
	string work_json;
	
	Master::ConfigWorkItem* pWorkItem = 0;
	pWorkItem = info.add_workconfiglist();
	pWorkItem->set_worknodeid("20000");
	pWorkItem->set_worktype(Common::EWorkNodeType_CybertronNodeTimer);
	if (!getRawString("D:\\simonetest\\2\\20000_CybertronNodeTimer.txt", work_json)) {
		return;
	}
	pWorkItem->set_workjson(work_json);
	pWorkItem->set_isstart(1);

	pWorkItem = info.add_workconfiglist();
	pWorkItem->set_worknodeid("20001");
	pWorkItem->set_worktype(Common::EWorkNodeType_CybertronBridgeIO);
	if (!getRawString("D:\\simonetest\\2\\20001_CybertronBridgeIO.txt", work_json)) {
		return;
	}
	pWorkItem->set_workjson(work_json);
	pWorkItem->set_isstart(1);

	pWorkItem = info.add_workconfiglist();
	pWorkItem->set_worknodeid("20002");
	pWorkItem->set_worktype(Common::EWorkNodeType_CybertronVehicleDynamic);
	if (!getRawString("D:\\simonetest\\2\\20002_VehicleDynamic.txt", work_json)) {
		return;
	}
	pWorkItem->set_workjson(work_json);
	pWorkItem->set_isstart(1);


	pWorkItem = info.add_workconfiglist();
	pWorkItem->set_worknodeid("20018");
	pWorkItem->set_worktype(Common::EWorkNodeType_CybertronVehicleDynamic);
	if (!getRawString("D:\\simonetest\\2\\20018_VehicleDynamic.txt", work_json)) {
		return;
	}
	pWorkItem->set_workjson(work_json);
	pWorkItem->set_isstart(1);


	pWorkItem = info.add_workconfiglist();
	pWorkItem->set_worknodeid("20003");
	pWorkItem->set_worktype(Common::EWorkNodeType_CybertronTrafficProvider);
	if (!getRawString("D:\\simonetest\\2\\20003_CybertronTrafficProvider.txt", work_json)) {
		return;
	}
	pWorkItem->set_workjson(work_json);
	pWorkItem->set_isstart(1);


	pWorkItem = info.add_workconfiglist();
	pWorkItem->set_worknodeid("20004");
	pWorkItem->set_worktype(Common::EWorkNodeType_CybertronBridgeSimOneDriver);
	if (!getRawString("D:\\simonetest\\2\\20004_CybertronBridgeSimOneDriver.txt", work_json)) {
		return;
	}
	pWorkItem->set_workjson(work_json);
	pWorkItem->set_isstart(1);

	pWorkItem = info.add_workconfiglist();
	pWorkItem->set_worknodeid("20014");
	pWorkItem->set_worktype(Common::EWorkNodeType_CybertronVisualizer);
	if (!getRawString("D:\\simonetest\\2\\20014_CybertronVisualizer.txt", work_json)) {
		return;
	}
	pWorkItem->set_workjson(work_json);
	pWorkItem->set_isstart(1);






	mpClient->send(Master::ETaskStartFromCommander, info);
}
void MasterTestClient::onFromMasterTaskStartRet(cybertron::Message& msg) {
	Master::TaskStartRetFromCommander result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	string taskId = result.taskid();

	Common::ESimOneErrorCode error_code = result.errorcode();

	string message = result.message();
	logInfo("[task start]task id:%s error_code:%d,message:%s", taskId.c_str(), error_code, message.c_str());
}

//task stop
void MasterTestClient::sendTaskStop() {
	Master::TaskStopFromCommander info;
	info.set_taskid(mTaskId);
	mpClient->send(Master::ETaskStopFromCommander, info);
}
void MasterTestClient::onFromTaskStopRet(cybertron::Message& msg) {
	Master::TaskStopRetFromCommander result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	string taskId = result.taskid();

	Common::ESimOneErrorCode error_code = result.errorcode();

	string message = result.message();
	logInfo("[task stop]task id:%s error_code:%d,message:%s", taskId.c_str(), error_code, message.c_str());
}

//work node start
void MasterTestClient::sendWorkNodeStart() {
}
void MasterTestClient::onFromWorkNodeStartRet(cybertron::Message& msg) {
}

//work node stop
void MasterTestClient::sendWorkNodeStop(){
}
void MasterTestClient::onFromWorkNodeStopRet(cybertron::Message& msg) {
}
void MasterTestClient::onFromSyncWorkNodeDynamicConfig(cybertron::Message& msg) {
	Common::SyncWorkNodeDynamicConfig result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	logInfo("onFromSyncWorkNodeDynamicConfig taskid%s work id%s work type%d,ip%s port:%d",
		result.taskid().c_str(), result.worknodeid().c_str(),result.worknodetype(),result.workpublicip().c_str(),result.workpublicport());
	return;
}
void MasterTestClient::onFromAgentStatusChange(cybertron::Message& msg) {
	Master::AgentStatusChange result;
	if (!msg.toProtobuf(result))
	{
		return;
	}

	Common::EAgentStatus status = result.status();
	logInfo("onFromAgentStatusChange agent status:%d", status);
}
void MasterTestClient::onFromTaskStateChange(cybertron::Message& msg) {
	Master::TaskStateChange result;
	if (!msg.toProtobuf(result))
	{
		return;
	}

	Common::ETaskState taskState = result.taskstate();
	string statestr = UtilStateHelper::getTaskStateName(taskState);
	logInfo("onFromTaskStateChange taskState:%s", statestr.c_str());
}
void MasterTestClient::onFromWorkStateChange(cybertron::Message& msg) {
	Master::WorkStateChange result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	string taskId = result.taskid();
	string workNodeId = result.worknodeid();
	Common::EWorkNodeState workState = result.workstate();
	string statestr = UtilStateHelper::getWorkNodeStateName(workState);
	logInfo("onFromWorkStateChange task id:%s work id:%s,workState:%s", taskId.c_str(), workNodeId.c_str(), statestr.c_str());
}
void MasterTestClient::onFromWorkStatusChange(cybertron::Message& msg) {
	Master::WorkStatusChange result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	string taskId = result.taskid();
	string workNodeId = result.worknodeid();
	Common::EWorkNodeStatus status = result.workstatus();
	string statestr = UtilStateHelper::getWorkNodeStatusName(status);
	
	logInfo("onFromWorkStatusChange task id:%s work id:%d,workState:%s", taskId.c_str(), workNodeId.c_str(), statestr.c_str());
}
void MasterTestClient::onFromWorkProgessChange(cybertron::Message& msg) {
	Master::WorkProgessChange result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	string taskId = result.taskid();
	int totalcount = result.totalcount();
	int successfulcount = result.successcount();
	logInfo("onFromWorkProgessChange task id:%s total:%d,sucess:%d", taskId.c_str(), totalcount, successfulcount);
}
void MasterTestClient::onFromEndTaskReasonChange(cybertron::Message& msg){
	Master::EndTaskReasonChange result;
	if (!msg.toProtobuf(result))
	{
		return;
	}
	string taskId = result.taskid();
	logInfo("=====> onFromEndTaskReasonChange task id :%s enter", taskId.c_str());
	auto data = result.reasonlist();
	for (int i = 0; i < data.size(); ++i)
	{
		logInfo("reason:%d", data[i]);
	}
	logInfo("<===== onFromEndTaskReasonChange task id :%s left", taskId.c_str());
}